package ids;

import java.util.Random;
import java.util.Vector;

import evo.TechInit;

import opt.FitnessFunction;
import opt.Move;
import opt.State;

import util.Nodes;

import ns2.Node;

public class PlacementState implements State {

	public long seed;

	public int temperatureCycle;

	public int innerMove;

	public float temperature;

	public float fitness;

	public Random rnd;

	public int[] placementAsInts;

	public Nodes nodes;

	public Vector<Sensor> sensors;
	public FitnessFunction myFitness;

	public void initialise(Object obj) {
		TechInit tob = (TechInit) obj;
		this.seed = tob.getSeed();
		this.rnd = new Random(this.seed);
		this.nodes = tob.getNodes();
		this.sensors = tob.getSensors();
		this.myFitness = new FitnessFunction();
		this.placementAsInts = new int[this.sensors.size()];
		for (int i = 0; i < this.sensors.size(); i++) {
			this.placementAsInts[i] = Math.abs(this.rnd.nextInt()
					% nodes.size());
		}
	}

	public float evalStateFitness() {
		for (int i = 0; i < this.sensors.size(); i++) {
			this.sensors.elementAt(i).setLocation(
					this.nodes.nodeAt(this.placementAsInts[i]));
			this.sensors.elementAt(i).run();
		}
		this.fitness = this.myFitness.evaluate(this.sensors);
		if (this.fitness == 5594) {
			this.rnd = new Random(this.seed);
		}
		return this.fitness;
	}

	public float getFitness() {
		return this.fitness;
	}

	public void acceptMove(Move m) {
		this.fitness = m.getFitness();
		this.placementAsInts[m.getIndexToMove()] = m.getNewValue();
	}

	public PlacementMove getNextMoveAndFitness() {
		PlacementMove m = new PlacementMove();
		// To create a backup of current state
		float currentFitness = this.fitness;
		int indexToMove = Math.abs(rnd.nextInt() % this.sensors.size());
		int currentValue = this.placementAsInts[indexToMove];

		// To find a neighbour node to move
		Node currentNode = this.nodes.getNode(currentValue);
		int index = Math.abs(rnd.nextInt() % currentNode.neighbours.size());
		int valueToSet = currentNode.neighbours.nodeAt(index).id;

		// int valueToSet = Math.abs(rnd.nextInt() %
		// this.topology.nodes.size());

		m.setIndexToMove(indexToMove);
		m.setNewValue(valueToSet);
		m.setFitness(evalMoveFitness(m));

		// To restore original state
		this.fitness = currentFitness;
		this.placementAsInts[indexToMove] = currentValue;
		return m;
	}

	public float evalMoveFitness(Move m) {
		this.placementAsInts[m.getIndexToMove()] = m.getNewValue();
		float mvFit = evalStateFitness();
		m.setFitness(mvFit);
		return mvFit;
	}

	public PlacementState copy() {
		PlacementState ps = new PlacementState();
		ps.seed = this.seed;
		ps.temperatureCycle = this.temperatureCycle;
		ps.innerMove = this.innerMove;
		ps.temperature = this.temperature;
		ps.fitness = this.fitness;
		ps.nodes = this.nodes;
		ps.sensors = this.sensors;
		ps.rnd = this.rnd;
		ps.placementAsInts = new int[this.placementAsInts.length];
		for (int i = 0; i < this.placementAsInts.length; i++) {
			ps.placementAsInts[i] = this.placementAsInts[i];
			ps.sensors.elementAt(i).setLocation(
					ps.nodes.nodeAt(ps.placementAsInts[i]));
			ps.sensors.elementAt(i).run();
		}
		return ps;
	}

	public String print() {
		StringBuffer result = new StringBuffer();
		result.append("The best placement ( score " + this.fitness + " ):\n");
		for (int i = 0; i < this.placementAsInts.length; i++) {
			result.append("Node "
					+ this.nodes.nodeAt(this.placementAsInts[i]).id + "\n");
		}
		result.append("was found at temperature cycle " + this.temperatureCycle
				+ ", inner move " + this.innerMove + ", seed " + this.seed
				+ "\n");
		return result.toString();
	}

	public void setTemperatureCycles(int temperatureCycle) {
		this.temperatureCycle = temperatureCycle;
	}

	public void setTemperature(float temperature) {
		this.temperature = temperature;
	}

	public void setInnerLoops(int innerMove) {
		this.innerMove = innerMove;
	}

	public void setFitness(float fitness) {
		this.fitness = fitness;
	}
}
